-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move AragonApp to unstructured storage #376
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main feedback is that the fact that we are using unstructured storage shouldn't change how one interacts with the contract. It is just an implementation detail that should be abstracted away.
contracts/apps/AppStorage.sol
Outdated
contract AppStorage is UnstructuredStorage { | ||
// keccak256("IKernel.kernel") | ||
bytes32 public constant kernelPosition = 0xe30296d9191ef86f01c8532636453e486db506a7be081a32b88c263fbc3a5e15; | ||
// keccak256("bytes32.appIdPosition") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be bytes32.appId
for consistency with other names.
contracts/apps/AppStorage.sol
Outdated
uint256[95] private storageOffset; // forces App storage to start at after 100 slots | ||
uint256 private offset; | ||
contract AppStorage is UnstructuredStorage { | ||
// keccak256("IKernel.kernel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove the type from the key names as types can change in the future and they are all storing a 32-byte word anyway.
It would make sense to have the type be explicit if we were storing more complex data types as arrays, mappings or structs, but for simple values I would remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add an aragonOS.appStorage
prefix to all keys to remove the possibility of collisions.
|
||
contract UnstructuredStorage { | ||
// keccak256("uint256.initializationBlock"), used by Initializable | ||
bytes32 public constant initializationBlockPosition = 0xd9120073d934f0c287a3a735b193740486dbba88578fd8f3cbb2b9a9b654e7ce; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not move to the Initializable
contract directly? Same comment on the naming of the key as for the other storage values in AppStorage
.
contracts/common/Initializable.sol
Outdated
_; | ||
} | ||
|
||
modifier isInitialized { | ||
require(initializationBlock > 0); | ||
require(getStorageUint256(initializationBlockPosition) > 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use getInitializationBlock()
contracts/apps/AppStorage.sol
Outdated
// keccak256("bytes32.appIdPosition") | ||
bytes32 public constant appIdPosition = 0x71ca50ba614d1d1a9a2433a5d0187a13e1a8c20bbe0013968dd089c71fd760eb; | ||
// keccak256("address.pinnedCode"), used by Proxy Pinned | ||
bytes32 public constant pinnedCodePosition = 0x0e2081dcdbb92be1037e55b39e913e70697c33a8a8b9bb01110e6f9e576089b8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the storage positions are an implementation detail that doesn't need to be exposed in the public API of the contract.
For all values I would add a getter and setter so the fact that AppStorage is using UnstructuredStorage is abstracted away from people interacting with the contract or deriving it:
function kernel() public view returns (IKernel) {
return IKernel(getStorageAddress(kernelPosition));
}
function setKernel(IKernel newKernel) internal {
setStorageAddress(kernelPosition, address(_kernel));
}
1cea212
to
a83d751
Compare
Addess PR 376 comments.
a83d751
to
5766201
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! Just some comments related with the tests mainly.
Let's wait for @sohkai's review before merging though
@@ -9,7 +9,7 @@ import "./IEVMScriptExecutor.sol"; | |||
import "./IEVMScriptRegistry.sol"; | |||
|
|||
import "../apps/AppStorage.sol"; | |||
|
|||
import "../kernel/IKernel.sol"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to import
test/unstructured_storage.js
Outdated
@@ -0,0 +1,61 @@ | |||
const getContract = name => artifacts.require(name) | |||
|
|||
contract('Constants', accounts => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'Constants' -> 'Unstructured storage'
test/unstructured_storage.js
Outdated
kernel = await getContract('Kernel').new() | ||
}) | ||
|
||
it('test', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would split into one test case per value being checked
test/unstructured_storage.js
Outdated
//checks | ||
assert.equal( | ||
parseInt(await web3.eth.getStorageAt(app.address, (await app.getInitializationBlockPosition())), 16), | ||
(await app.getInitializationBlock.call()).toString(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can check what the block number was after app.initialize()
and assert equality with the value stored at the storage position.
test/unstructured_storage.js
Outdated
|
||
//checks | ||
assert.equal( | ||
parseInt(await web3.eth.getStorageAt(app.address, (await app.getInitializationBlockPosition())), 16), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would get the positions from test/mocks/KeccakConstants.sol
rather than from 'AragonApp', even though that equality already tested in test/keccak_constants.js
. Would allow to remove all the position public getters from AppStubStorage
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, but if we remove the public getters from AppStubStorage
then tests in keccak_constants.js
won't work. Here I see we are testing 2 things:
- That hardcoded position is what it is intended to be (not a big deal, but better to have it, to avoid collisions)
- That things are properly stored at those positions.
Testing the second one, the first one would be implicitly tested if we use positions from KeccaKConstants
but I think it's better to have both for 2 reasons: 1) in case of failing, it's eaiser to know what the problem is, 2) we could be pointing at another position that has the same value (yes, unlikely, but still) and having both tests we would have more chances to catch it.
(And AppSutbStorage
is just for testing, so no big deal having those getters there)
Address PR 376 comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 Really loving this, and the tests :D.
Have a few comments that I'll address :).
contracts/apps/AppStorage.sol
Outdated
setStorageAddress(pinnedCodePosition, _pinnedCode); | ||
} | ||
|
||
function pinnedCode() internal view returns (address) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the pinnedCode
could go into AppProxyPinned
, since it's only relevant there.
pragma solidity ^0.4.18; | ||
|
||
|
||
contract UnstructuredStorage { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could make this a library, and remove all public
functions from it (I don't think there's a big benefit in having the getters be public).
migrations/2_apm.js
Outdated
@@ -36,7 +36,7 @@ module.exports = function (deployer, network, accounts, arts = null) { | |||
console.log('Deployed APM at:', apmAddr) | |||
|
|||
const apm = getContract('APMRegistry').at(apmAddr) | |||
console.log('Kernel:', await apm.kernel()) | |||
console.log('Kernel:', await apm.kernel.call()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think these .call()
s are unnecessary; I'll try removing them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, they are for testrpc
but I had some problems without them using geth
on our dao-kits, so now I'm putting them whenever I remember, so in case we finally move our tests to geth they will work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, weird, this should be handled by truffle (truffle-contract changes its interface based on whether the function is marked constant
or not), rather than a testrpc / geth issue.
This change should be transparent to the DAO Kits and apps. |
For deployment cost:
Ouch :(. I'd say this change is worth it though. |
* Fix tests after linting * Remove unnecessary .call() in tests and migrations * Make UnstructuredStorage a library * Move pinnedCode storage only to AppProxyPinned
Current deployment costs:
|
Fixes #342
TODO:
Do the same forKernel